/******************************************************************************

    Copyright (C) 2006 Alessio Stalla (alessiostalla@gmail.com)

    This file is part of PrettyProlog.

    PrettyProlog is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    PrettyProlog is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with PrettyProlog; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

    See the file COPYING for further details.

 *****************************************************************************/

package prettyprolog.gui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.util.LinkedList;

import javax.swing.ImageIcon;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;

import prettyprolog.engine.listeners.StackEvent;
import prettyprolog.engine.listeners.StackListener;

/** A panel showing an SLD tree. */
public class SLDViewer extends PrettyPrologPanel {

	private static final long serialVersionUID = 1L;

	@SuppressWarnings("serial")
	private class NodeRenderer extends DefaultTreeCellRenderer {

		private ImageIcon cut_icon = null;
		private ImageIcon sol_icon = null;
		private ImageIcon fail_icon = null;
		private ImageIcon goal_icon = null;

		protected NodeRenderer() {
			java.net.URL url = getClass().getResource("/prettyprolog.gui.images/cut.png");
			if (url != null) {
				//Image image = Toolkit.getDefaultToolkit().getImage(url);
				cut_icon = new ImageIcon(url);
			}
			url = getClass().getResource("/prettyprolog.gui.images/solution.png");
			if (url != null) {
				//Image image = Toolkit.getDefaultToolkit().getImage(url);
				sol_icon = new ImageIcon(url);
			}
			url = getClass().getResource("/prettyprolog.gui.images/dead_end.png");
			if (url != null) {
				//Image image = Toolkit.getDefaultToolkit().getImage(url);
				fail_icon = new ImageIcon(url);
			}
			url = getClass().getResource("/prettyprolog.gui.images/goal.png");
			if (url != null) {
				//Image image = Toolkit.getDefaultToolkit().getImage(url);
				goal_icon = new ImageIcon(url);
			}
		}

		public Component getTreeCellRendererComponent(JTree tree, Object value,
				boolean sel, boolean expanded, boolean leaf, int row,
				boolean hasFocus) {
			super.getTreeCellRendererComponent(tree, value, sel, expanded,
					leaf, row, hasFocus);
			setText(((SLDNode) value).getUserObject().toString());
			setFont(getFont().deriveFont(Font.PLAIN));
			if (goal_icon != null)
				setIcon(goal_icon);
			if (leaf && ((SLDNode) value).getType() == SLDNode.SOLUTION) {
				if (sol_icon != null)
					setIcon(sol_icon);
				setForeground(new Color(0x40, 0xc0, 0x40));
			} else if (leaf && ((SLDNode) value).getType() == SLDNode.DEAD_END) {
				if (fail_icon != null)
					setIcon(fail_icon);
				setForeground(new Color(0xc0, 0x40, 0x40));
			} else if (((SLDNode) value).getType() == SLDNode.CUT) {
				if (cut_icon != null)
					setIcon(cut_icon);
				setFont(getFont().deriveFont(Font.ITALIC));
			}
			return this;
		}
	}

	@SuppressWarnings("serial")
	private class SLDNode extends DefaultMutableTreeNode {

		static final byte NORMAL = 0, DEAD_END = 1, SOLUTION = 2, CUT = 3;

		private byte type = NORMAL;
		private prettyprolog.engine.Frame frame = null;

		SLDNode() {
			super();
		}

		SLDNode(Object user_object) {
			super(user_object);
		}

		public Object getUserObject() {
			if (frame == null)
				return super.getUserObject();
			else {
				String s;
				if (frame.getSubstitution() == null)
					s = "fallimento!!!";
				else
					s = frame.getSubstitution().toString(show_hidden_variables);
				return (frame.getGoal() + "; " + s);
			}
		}

		public void setUserObject(Object obj) {
			if (obj instanceof prettyprolog.engine.Frame)
				frame = (prettyprolog.engine.Frame) obj;
			else
				super.setUserObject(obj);
		}

		void setType(byte t) {
			type = t;
		}

		byte getType() {
			return type;
		}

		public String toString() {
			return getUserObject() + "";
		}
	}

	private SLDNode root = new SLDNode("SLD tree");
	private DefaultTreeModel tree_model = new DefaultTreeModel(root);
	private JTree sld_tree = new JTree(tree_model);
	private java.util.List<SLDNode> nodes = new LinkedList<SLDNode>();
	private prettyprolog.engine.Stack stack;

	public SLDViewer() {
		super("SLD Viewer");
		setLayout(new BorderLayout());
		sld_tree.setCellRenderer(new NodeRenderer());
		add(new JScrollPane(sld_tree));
		nodes.add(root);
		// add(show_hidden_vars_b, BorderLayout.SOUTH);
	}

	public void showHiddenVariables(boolean show) {
		super.showHiddenVariables(show);
		tree_model.reload();
	}

	private StackListener stack_listener = new StackListener() {
		public void framePopped(StackEvent e) {
			SLDNode node = nodes.remove(nodes.size() - 1);
			if (nodes.get(nodes.size() - 1).getType() == SLDNode.CUT)
				nodes.remove(nodes.size() - 1);
			if (node != null)
				if (node.getType() != SLDNode.SOLUTION) {
					node.setType(SLDNode.DEAD_END);
					sld_tree.revalidate();
					sld_tree.repaint();
				}
			// GUI UPDATING PROBLEM HERE ^^^
		}

		public void frameCut(StackEvent e) {
			if ((stack.getSize() + 1) == nodes.size() - 1)
				nodes.get(nodes.size() - 1).setType(SLDNode.CUT);
			else {
				SLDNode node = nodes.remove(stack.getSize() + 1);
				node.setType(SLDNode.CUT);
			}
		}

		public void framePushed(StackEvent e) {
			SLDNode node = new SLDNode();
			node.setUserObject(e.getFrame());
			nodes.add(node);
			if (e.getFrame().getGoal().isEmpty())
				node.setType(SLDNode.SOLUTION);
			SLDNode parent = nodes.get(nodes.size() - 2);
			tree_model.insertNodeInto(node, parent, parent.getChildCount());
			sld_tree.scrollPathToVisible(new TreePath(node.getPath()));
		}

		public void stackCleared(StackEvent e) {
			root = new SLDNode("SLD tree");
			nodes = new LinkedList<SLDNode>();
			nodes.add(root);
			tree_model = new DefaultTreeModel(root);
			sld_tree.setModel(tree_model);
		}
	};

	public void setOwner(PrettyPrologApplet o) {
		super.setOwner(o);
		stack = owner.getEngine().getStack();
		stack.addStackListener(stack_listener);
	}

	public void removeFromOwner() {
		if (owner != null)
			stack.removeStackListener(stack_listener);
		super.removeFromOwner();
	}

}